package com.media.system.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.media.system.common.utils.DateUtils;
import com.media.system.common.utils.SecurityUtils;
import com.media.system.common.utils.StringUtils;
import com.media.system.domain.MediaFile;
import com.media.system.mapper.MediaFileMapper;
import com.media.system.service.MediaFileService;
import com.media.system.service.FileStorageService;
import com.media.system.service.AiTagService;
import com.media.system.service.dto.FileUploadRequest;
import com.media.system.service.dto.AiTagResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 媒体文件服务实现
 * 
 * @author Claude
 * @since 2024-01-20
 */
@Service
@Slf4j
public class MediaFileServiceImpl extends ServiceImpl<MediaFileMapper, MediaFile> implements MediaFileService {

    @Autowired
    private MediaFileMapper mediaFileMapper;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private AiTagService aiTagService;

    @Value("${media.system.upload.path:./uploads}")
    private String uploadPath;

    @Value("${media.system.upload.prefix:http://localhost:8080/uploads}")
    private String urlPrefix;

    @Value("${media.system.upload.max-file-size:2147483648}")
    private Long maxFileSize;

    // 支持的文件类型
    private static final Map<String, String> ALLOWED_EXTENSIONS = new HashMap<String, String>() {{
        // 图片文件
        put("jpg", "image"); put("jpeg", "image"); put("png", "image"); 
        put("gif", "image"); put("bmp", "image"); put("webp", "image");
        // 视频文件
        put("mp4", "video"); put("avi", "video"); put("mov", "video");
        put("wmv", "video"); put("flv", "video"); put("mkv", "video");
        // 音频文件
        put("mp3", "audio"); put("wav", "audio"); put("flac", "audio"); put("aac", "audio");
        // 文档文件
        put("pdf", "document"); put("doc", "document"); put("docx", "document");
        put("xls", "document"); put("xlsx", "document"); put("txt", "document");
    }};

    @Override
    @Transactional
    public MediaFile uploadFile(MultipartFile file, FileUploadRequest request) {
        if (file.isEmpty()) {
            throw new RuntimeException("上传文件不能为空");
        }

        // 文件大小检查
        if (file.getSize() > maxFileSize) {
            throw new RuntimeException("文件大小超过限制: " + (maxFileSize / 1024 / 1024) + "MB");
        }

        // 获取文件信息
        String originalFileName = file.getOriginalFilename();
        String fileExtension = FileUtil.extName(originalFileName).toLowerCase();
        String contentType = file.getContentType();

        // 文件类型检查
        if (!ALLOWED_EXTENSIONS.containsKey(fileExtension)) {
            throw new RuntimeException("不支持的文件类型: " + fileExtension);
        }

        try {
            // 计算文件哈希值
            String fileHash = DigestUtil.md5Hex(file.getInputStream());
            
            // 检查重复文件
            Long currentUserId = SecurityUtils.getUserId();
            MediaFile existingFile = checkDuplicateFile(fileHash, currentUserId);
            if (existingFile != null && "skip".equals(request.getDuplicateHandling())) {
                return existingFile;
            }

            // 生成文件名和路径
            String fileName = generateFileName(originalFileName, request.getDuplicateHandling());
            String objectName = generateObjectName(fileName, request.getFolderPath());

            // 上传文件到MinIO
            String fileUrl = fileStorageService.uploadFile(file, objectName);

            // 创建媒体文件记录
            MediaFile mediaFile = new MediaFile();
            mediaFile.setUserId(currentUserId);
            mediaFile.setFamilyId(request.getFamilyId());
            mediaFile.setFileName(fileName);
            mediaFile.setOriginalFileName(originalFileName);
            mediaFile.setFilePath(objectName);
            mediaFile.setFileUrl(fileUrl);
            mediaFile.setFileType(ALLOWED_EXTENSIONS.get(fileExtension));
            mediaFile.setFileFormat(fileExtension);
            mediaFile.setMimeType(contentType);
            mediaFile.setFileSize(file.getSize());
            mediaFile.setFileHash(fileHash);
            mediaFile.setUserTags(request.getUserTags());
            mediaFile.setLatitude(request.getLatitude());
            mediaFile.setLongitude(request.getLongitude());
            mediaFile.setLocation(request.getLocation());
            mediaFile.setStatus(0);
            mediaFile.setIsPublic(request.getIsPublic());
            mediaFile.setIsFavorite(0);
            mediaFile.setDownloadCount(0);
            mediaFile.setViewCount(0);
            mediaFile.setRemark(request.getRemark());

            // 处理图片特殊信息
            if ("image".equals(mediaFile.getFileType())) {
                processImageMetadata(mediaFile, file);
            }

            // 处理视频特殊信息
            if ("video".equals(mediaFile.getFileType())) {
                processVideoMetadata(mediaFile, file);
            }

            // 生成缩略图
            if (request.getGenerateThumbnail() && canGenerateThumbnail(mediaFile.getFileType())) {
                String thumbnailObjectName = generateThumbnailObjectName(objectName);
                String thumbnailUrl = generateThumbnail(file, mediaFile, thumbnailObjectName);
                mediaFile.setThumbnailPath(thumbnailObjectName);
                mediaFile.setThumbnailUrl(thumbnailUrl);
            }

            // 保存到数据库
            save(mediaFile);

            // 异步处理AI标签识别
            if (request.getEnableAiTags()) {
                processAiTagsAsync(mediaFile);
            }

            log.info("文件上传成功: {}, 大小: {}B", originalFileName, file.getSize());
            return mediaFile;

        } catch (IOException e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional
    public List<MediaFile> batchUploadFiles(List<MultipartFile> files, FileUploadRequest request) {
        List<MediaFile> uploadedFiles = new ArrayList<>();
        
        for (MultipartFile file : files) {
            try {
                MediaFile mediaFile = uploadFile(file, request);
                uploadedFiles.add(mediaFile);
            } catch (Exception e) {
                log.error("批量上传文件失败: {}, 错误: {}", file.getOriginalFilename(), e.getMessage());
                // 继续处理其他文件，不中断整个批量上传
            }
        }
        
        return uploadedFiles;
    }

    @Override
    public Page<MediaFile> selectMediaFilesByUserId(Page<MediaFile> page, Long userId, String fileType) {
        LambdaQueryWrapper<MediaFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MediaFile::getUserId, userId)
                .eq(MediaFile::getStatus, 0)
                .eq(MediaFile::getDeleted, false);
        
        if (StringUtils.isNotEmpty(fileType)) {
            wrapper.eq(MediaFile::getFileType, fileType);
        }
        
        wrapper.orderByDesc(MediaFile::getCreateTime);
        return page(page, wrapper);
    }

    @Override
    public Page<MediaFile> selectMediaFilesByFamilyId(Page<MediaFile> page, Long familyId) {
        LambdaQueryWrapper<MediaFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MediaFile::getFamilyId, familyId)
                .eq(MediaFile::getIsPublic, 1)
                .eq(MediaFile::getStatus, 0)
                .eq(MediaFile::getDeleted, false)
                .orderByDesc(MediaFile::getCreateTime);
        
        return page(page, wrapper);
    }

    @Override
    public Page<MediaFile> searchMediaFiles(Page<MediaFile> page, Long userId, String keyword) {
        LambdaQueryWrapper<MediaFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MediaFile::getUserId, userId)
                .eq(MediaFile::getStatus, 0)
                .eq(MediaFile::getDeleted, false);
        
        if (StringUtils.isNotEmpty(keyword)) {
            wrapper.and(w -> w.like(MediaFile::getFileName, keyword)
                    .or().like(MediaFile::getOriginalFileName, keyword)
                    .or().like(MediaFile::getUserTags, keyword)
                    .or().like(MediaFile::getAiTags, keyword)
                    .or().like(MediaFile::getLocation, keyword));
        }
        
        wrapper.orderByDesc(MediaFile::getCreateTime);
        return page(page, wrapper);
    }

    @Override
    public Page<MediaFile> selectMediaFilesByTags(Page<MediaFile> page, Long userId, String tags) {
        List<MediaFile> files = mediaFileMapper.selectMediaFilesByTags(tags, userId);
        // 这里简化处理，实际应该实现分页
        Page<MediaFile> result = new Page<>(page.getCurrent(), page.getSize());
        result.setRecords(files);
        result.setTotal(files.size());
        return result;
    }

    @Override
    public List<MediaFile> getRecentMediaFiles(Long userId, Integer limit) {
        return mediaFileMapper.selectRecentMediaFiles(userId, limit);
    }

    @Override
    public Page<MediaFile> getFavoriteMediaFiles(Page<MediaFile> page, Long userId) {
        LambdaQueryWrapper<MediaFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MediaFile::getUserId, userId)
                .eq(MediaFile::getIsFavorite, 1)
                .eq(MediaFile::getStatus, 0)
                .eq(MediaFile::getDeleted, false)
                .orderByDesc(MediaFile::getCreateTime);
        
        return page(page, wrapper);
    }

    @Override
    @Transactional
    public boolean toggleFavorite(Long fileId, Long userId) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return false;
        }
        
        mediaFile.setIsFavorite(mediaFile.getIsFavorite() == 1 ? 0 : 1);
        return updateById(mediaFile);
    }

    @Override
    @Transactional
    public boolean deleteMediaFile(Long fileId, Long userId) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return false;
        }
        
        // 删除物理文件
        deletePhysicalFile(mediaFile);
        
        // 删除数据库记录
        return removeById(fileId);
    }

    @Override
    @Transactional
    public boolean batchDeleteMediaFiles(List<Long> fileIds, Long userId) {
        return mediaFileMapper.batchDeleteFiles(fileIds, userId) > 0;
    }

    @Override
    @Transactional
    public boolean moveToTrash(Long fileId, Long userId) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return false;
        }
        
        mediaFile.setStatus(2); // 状态设为已删除
        return updateById(mediaFile);
    }

    @Override
    @Transactional
    public boolean restoreFromTrash(Long fileId, Long userId) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return false;
        }
        
        mediaFile.setStatus(0); // 状态恢复为正常
        return updateById(mediaFile);
    }

    @Override
    public String generateShareLink(Long fileId, Long userId, Integer expireHours) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return null;
        }
        
        // 生成分享链接逻辑
        String shareToken = IdUtil.fastSimpleUUID();
        // TODO: 将分享信息存储到Redis或数据库
        return "/share/" + shareToken;
    }

    @Override
    @Transactional
    public boolean updateFileTags(Long fileId, Long userId, String tags) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null || !mediaFile.getUserId().equals(userId)) {
            return false;
        }
        
        mediaFile.setUserTags(tags);
        return updateById(mediaFile);
    }

    @Override
    @Transactional
    public boolean updateAiTags(Long fileId, String aiTags) {
        MediaFile mediaFile = getById(fileId);
        if (mediaFile == null) {
            return false;
        }
        
        mediaFile.setAiTags(aiTags);
        return updateById(mediaFile);
    }

    @Override
    public Map<String, Object> getStorageStats(Long userId) {
        List<Map<String, Object>> stats = mediaFileMapper.selectStorageStatsByUserId(userId);
        Map<String, Object> result = new HashMap<>();
        
        long totalSize = 0;
        int totalCount = 0;
        
        for (Map<String, Object> stat : stats) {
            String fileType = (String) stat.get("file_type");
            Long size = (Long) stat.get("total_size");
            Integer count = (Integer) stat.get("file_count");
            
            result.put(fileType + "_size", size);
            result.put(fileType + "_count", count);
            
            totalSize += size;
            totalCount += count;
        }
        
        result.put("total_size", totalSize);
        result.put("total_count", totalCount);
        
        return result;
    }

    @Override
    public MediaFile checkDuplicateFile(String fileHash, Long userId) {
        return mediaFileMapper.selectMediaFileByHash(fileHash, userId);
    }

    @Override
    public void incrementViewCount(Long fileId) {
        mediaFileMapper.updateViewCount(fileId);
    }

    @Override
    public void incrementDownloadCount(Long fileId) {
        mediaFileMapper.updateDownloadCount(fileId);
    }

    @Override
    public Page<MediaFile> selectMediaFilesByTimeRange(Page<MediaFile> page, Long userId, String startTime, String endTime) {
        List<MediaFile> files = mediaFileMapper.selectMediaFilesByTimeRange(userId, startTime, endTime);
        // 简化分页处理
        Page<MediaFile> result = new Page<>(page.getCurrent(), page.getSize());
        result.setRecords(files);
        result.setTotal(files.size());
        return result;
    }

    /**
     * 生成唯一文件名
     */
    private String generateFileName(String originalFileName, String duplicateHandling) {
        String extension = FileUtil.extName(originalFileName);
        String baseName = FileUtil.getPrefix(originalFileName);
        
        if ("rename".equals(duplicateHandling)) {
            return baseName + "_" + System.currentTimeMillis() + "." + extension;
        }
        
        return IdUtil.fastSimpleUUID() + "." + extension;
    }

    /**
     * 生成文件保存路径
     */
    private String generateFilePath(String fileName, String folderPath) {
        String datePath = DateUtils.datePath();
        
        if (StringUtils.isNotEmpty(folderPath)) {
            return folderPath + File.separator + datePath + File.separator + fileName;
        }
        
        return datePath + File.separator + fileName;
    }

    /**
     * 生成对象存储名称
     */
    private String generateObjectName(String fileName, String folderPath) {
        String datePath = DateUtils.datePath();
        
        if (StringUtils.isNotEmpty(folderPath)) {
            return folderPath + "/" + datePath + "/" + fileName;
        }
        
        return datePath + "/" + fileName;
    }

    /**
     * 生成缩略图对象名称
     */
    private String generateThumbnailObjectName(String originalObjectName) {
        int lastSlashIndex = originalObjectName.lastIndexOf('/');
        String path = lastSlashIndex > 0 ? originalObjectName.substring(0, lastSlashIndex + 1) : "";
        String fileName = lastSlashIndex > 0 ? originalObjectName.substring(lastSlashIndex + 1) : originalObjectName;
        
        String extension = FileUtil.extName(fileName);
        String baseName = FileUtil.getPrefix(fileName);
        
        return path + "thumbnails/" + baseName + "_thumb." + extension;
    }

    /**
     * 处理图片元数据
     */
    private void processImageMetadata(MediaFile mediaFile, MultipartFile imageFile) {
        // TODO: 使用图片处理库提取EXIF信息
        // 这里简化处理，实际应该提取图片的宽度、高度、拍摄时间、GPS信息、相机信息等
    }

    /**
     * 处理视频元数据
     */
    private void processVideoMetadata(MediaFile mediaFile, MultipartFile videoFile) {
        // TODO: 使用视频处理库提取视频信息
        // 这里简化处理，实际应该提取视频的时长、分辨率、编码格式等
    }

    /**
     * 判断是否可以生成缩略图
     */
    private boolean canGenerateThumbnail(String fileType) {
        return "image".equals(fileType) || "video".equals(fileType);
    }

    /**
     * 生成缩略图
     */
    private String generateThumbnail(MultipartFile sourceFile, MediaFile mediaFile, String thumbnailObjectName) {
        // TODO: 实现缩略图生成逻辑
        // 这里简化处理，实际应该根据文件类型生成对应的缩略图
        // 生成缩略图并上传到MinIO，返回缩略图URL
        try {
            // 模拟缩略图生成，实际应使用图片处理库
            String thumbnailUrl = fileStorageService.getFileUrl(thumbnailObjectName);
            return thumbnailUrl;
        } catch (Exception e) {
            log.error("生成缩略图失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 异步处理AI标签识别
     */
    @Async
    private void processAiTagsAsync(MediaFile mediaFile) {
        try {
            log.info("开始为文件 {} 进行AI标签识别", mediaFile.getFileName());
            
            AiTagResult result;
            switch (mediaFile.getFileType().toLowerCase()) {
                case "image":
                    result = aiTagService.recognizeImageTags(mediaFile);
                    break;
                case "video":
                    result = aiTagService.recognizeVideoTags(mediaFile);
                    break;
                case "audio":
                    result = aiTagService.recognizeAudioTags(mediaFile);
                    break;
                default:
                    log.info("文件类型 {} 不支持AI标签识别", mediaFile.getFileType());
                    return;
            }
            
            // 更新AI标签到数据库
            if ("success".equals(result.getStatus()) && result.getTags() != null) {
                String aiTags = result.getTags().stream()
                        .map(AiTagResult.AiTag::getName)
                        .reduce((a, b) -> a + "," + b)
                        .orElse("");
                        
                updateAiTags(mediaFile.getFileId(), aiTags);
                log.info("文件 {} AI标签识别完成，识别到标签: {}", mediaFile.getFileName(), aiTags);
            } else {
                log.warn("文件 {} AI标签识别失败: {}", mediaFile.getFileName(), result.getErrorMessage());
            }
            
        } catch (Exception e) {
            log.error("文件 {} AI标签识别异常: {}", mediaFile.getFileName(), e.getMessage(), e);
        }
    }

    /**
     * 删除物理文件
     */
    private void deletePhysicalFile(MediaFile mediaFile) {
        try {
            // 删除主文件
            if (StringUtils.isNotEmpty(mediaFile.getFilePath())) {
                fileStorageService.deleteFile(mediaFile.getFilePath());
            }
            
            // 删除缩略图
            if (StringUtils.isNotEmpty(mediaFile.getThumbnailPath())) {
                fileStorageService.deleteFile(mediaFile.getThumbnailPath());
            }
        } catch (Exception e) {
            log.error("删除物理文件失败: {}", e.getMessage(), e);
        }
    }
}